package org.bronycorps.mlptss.domain.layer;

import java.util.ArrayList;
import java.util.List;

import javax.script.ScriptEngine;

import org.bronycorps.mlptss.MlpTss;
import org.bronycorps.mlptss.domain.actor.AbstractActor;
import org.bronycorps.mlptss.domain.actor.EnemyActor;
import org.bronycorps.mlptss.domain.actor.MissileActor;
import org.bronycorps.mlptss.domain.actor.SpecialMissileActor;
import org.bronycorps.mlptss.domain.controller.MissileController;
import org.bronycorps.mlptss.domain.model.Missile;
import org.bronycorps.mlptss.domain.model.Player;
import org.bronycorps.mlptss.domain.model.event.CreateBossEvent;
import org.bronycorps.mlptss.domain.model.event.CreateBossEventListener;
import org.bronycorps.mlptss.domain.model.event.CreateMissileEvent;
import org.bronycorps.mlptss.domain.model.event.CreateMissileEventListener;
import org.bronycorps.mlptss.domain.model.event.CreateSpecialMissileEvent;
import org.bronycorps.mlptss.domain.model.event.CreateSpecialMissileEventListener;
import org.bronycorps.mlptss.domain.model.event.StateChangeEvent;
import org.bronycorps.mlptss.domain.model.event.StateChangeEventListener;
import org.bronycorps.mlptss.domain.model.script.SpecialMissileScriptMediator;
import org.bronycorps.mlptss.manager.TrajectFactory;
import org.bronycorps.mlptss.manager.TrajectFactory.MissileTrajectType;
import org.bronycorps.mlptss.manager.ai.PredictionManager;
import org.bronycorps.mlptss.manager.preferences.GameConfiguration;
import org.bronycorps.mlptss.manager.scripting.ScriptManager;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2;

public class MissileLayer extends AbstractLayer implements
		CreateMissileEventListener, CreateSpecialMissileEventListener,
		CreateBossEventListener, StateChangeEventListener {

	private static final int MISSILE_REBOUNCE_LIMIT = 4;

	private float chainUpgradeFactor;

	private final ActorLayer master;

	private final List<MissileActor> missiles = new ArrayList<MissileActor>();
	private final List<SpecialMissileActor> specialMissiles = new ArrayList<SpecialMissileActor>();

	public MissileLayer(ActorLayer master) {
		super();
		this.master = master;

		for (int i = 0; i < 2; i++) {
			master.getTrack(i).addListener(this);
			Player player = (Player) master.getTrack(i).getPlayer().getModel();
			player.addListener((CreateSpecialMissileEventListener) this);
			player.addListener((CreateBossEventListener) this);

		}

		this.chainUpgradeFactor = GameConfiguration.getInstance()
				.getMissileChainUpgradeFactor();
	}

	public void addSpecialMissile(SpecialMissileActor actor) {
		this.specialMissiles.add(actor);

		addActor(actor);
	}

	public List<MissileActor> getMissiles() {
		return this.missiles;
	}

	public List<SpecialMissileActor> getSpecialMissiles() {
		return this.specialMissiles;
	}

	@Override
	public void onCreateBoss(CreateBossEvent event) {
		Player source = event.getPlayer();
		Track sourceTrack = this.master.getPlayerTrack(event.getPlayer());

		Track destinationTrack = this.master.getOppositeTrackFrom(sourceTrack);

		destinationTrack.createBoss(source, sourceTrack);
	}

	@Override
	public void onCreateMissile(CreateMissileEvent event) {

		float upgradeFactor = 1;
		AbstractActor actor = null;
		float offset = 0;
		int rebounceCount = 0;
		if (event.getSource() instanceof EnemyActor) {
			actor = event.getEnemyActor();
			offset = actor.getTrack().getY();
		} else if (event.getSource() instanceof MissileActor) {
			actor = event.getMissileActor();
			upgradeFactor = this.chainUpgradeFactor;
			rebounceCount = ((Missile) actor.getModel()).getRebounceCount() + 1;
		}
		Track track = event.getTrack();
		Track oppositeTrack = this.master.getOppositeTrackFrom(track);

		// check if missile rebounce went to its limit to become special missile
		if (rebounceCount > MISSILE_REBOUNCE_LIMIT) {
			CreateSpecialMissileEvent e = new CreateSpecialMissileEvent(
					(Player) track.getPlayer().getModel(), 1);
			onCreateSpecialMissile(e);
		} else {

			Vector2 initialPosition = actor.getModel().getPosition().cpy()
					.add(0, offset);

			MissileActor missile = new MissileActor(new MissileController(
					new Missile((Player) track.getPlayer().getModel(),
							rebounceCount)), oppositeTrack);

			missile.setInitialPosition(initialPosition.x, initialPosition.y);
			missile.setScale((actor.getScaleX() + 0.2f) * upgradeFactor);

			addActor(missile);

			this.missiles.add(missile);

			TrajectFactory trajectFactory = TrajectFactory.getInstance();

			trajectFactory.generateMissileTraject(missile, initialPosition,
					oppositeTrack.getPlayer(), MissileTrajectType.STRAIGHT);

			trajectFactory.tweenMissile(missile);

			if (oppositeTrack.getPredictionManager() != null) {
				oppositeTrack.getPredictionManager().addMissile(missile);
				oppositeTrack.clearAISnapshots();
			}
		}
	}

	@Override
	public void onCreateSpecialMissile(CreateSpecialMissileEvent event) {
		// get player
		Player player = (Player) event.getSource();
		Track track = this.master.getPlayerTrack(player);
		String scriptName = player.getCharacter().getScriptName();

		// get script object
		ScriptEngine engine = ScriptManager.getInstance().getScript(scriptName);

		// add the mediator the the script
		SpecialMissileScriptMediator jsStage = new SpecialMissileScriptMediator(
				this, track, this.master.getOppositeTrackFrom(track),
				event.getCount());

		ScriptManager.scheduleScript(engine, jsStage);

	}

	@Override
	public void onStateChange(StateChangeEvent event) {

	}

	public void removeMissile(MissileActor missileActor) {
		PredictionManager predictionManager = missileActor.getTrack()
				.getPredictionManager();

		this.missiles.remove(missileActor);
		removeActor(missileActor);
		missileActor.clearActions();
		TrajectFactory.getInstance().clearTweens(missileActor);

		if (predictionManager != null) {
			predictionManager.removeMissile(missileActor);

			missileActor.getTrack().clearAISnapshots();
		}
	}

	public void removeSpecialMissile(SpecialMissileActor specialMissileActor) {
		PredictionManager predictionManager = specialMissileActor.getTrack()
				.getPredictionManager();

		Gdx.app.log(MlpTss.LOG, "removed special missile!");

		this.specialMissiles.remove(specialMissileActor);
		removeActor(specialMissileActor);
		specialMissileActor.clearActions();

		TrajectFactory.getInstance().clearTweens(specialMissileActor);

		if (predictionManager != null) {
			predictionManager.removeSpecialMissile(specialMissileActor);

			specialMissileActor.getTrack().clearAISnapshots();
		}
	}

}
